#include <pantheios/pantheios.hpp>
#include <pantheios/inserters.hpp>
#include "racing_game_logic.h"
#include <iltmessage.h>
#include <iltclient.h>
#include <iltdrawprim.h>
#include <iltvideomgr.h>
#include <iltsoundmgr.h>
#include "pageInfoGetter.hpp"
#include "valuerange.hpp"

#include "clientinterfaces.h"
#include "ltclientshell.h"
#include "msgids.h"
#include "automessage.h"
#include "shader_mgr.h"
#include "camera/back_camera.h"
#include "camera/camera.h"
#include "screen/dash_board.h"
#include "skybox_lens/lens_flare.h"
#include "skybox_lens/sky_box.h"
#include "billboard/trace_pool.h"
#include "billboard/grass_control.h"
#include "adapter_car.h"
#include "shader/blur_seter.h"
#include "shader/shader_light_system.h"
#include "shader_light.h"
#include "Video_mgr.h"
#include "ltobjectcreate.h"
#include "road_node_maker.h"
#include "road_node_map.h"


void racing_game_logic::on_event(int event_type) {
    if(event_type == 3)
        m_shader_mgr->recreate() ;
}



void racing_game_logic::do_render_3d(float frame_time) {
   // pantheios::log_NOTICE("before render ..........") ;
    
	
    g_pLTCDrawPrim->SetCamera(shell()->screen_camera()->camera_handle()) ;
    
    if(m_shader_mgr->if_full_render()) {
        //g_pLTClient->CPrint("render if_full_render") ;
        m_shader_mgr->render(frame_time) ;
    } else {

        g_pLTClient->RenderCamera(shell()->screen_camera()->camera_handle(), frame_time) ;
        m_trace_pool->render() ;
    }

    m_video_mgr->render_video();
   // pantheios::log_NOTICE("end render...........") ;
}

void racing_game_logic::respond_bind_shader_message(ILTMessage_Read* pMsg) {

    // this is a test part
    // FIXME
    
    
    LTObject* obj = pMsg->ReadObject() ;
    int num = pMsg->Readint32() ;
    int first = -1 ;
    std::vector<int> ids ;

    for(int i = 0 ; i < num ; i++) ids.push_back(pMsg->Readint32()) ;
    pantheios::log_DEBUG("s -> c car(",
                         pantheios::hex_ptr(obj), ") ",
                         "attach shaders(num is ", pantheios::integer(num),
                         ") first is ", pantheios::integer(first)) ;
    if(obj && m_adapter_objects.find(obj) == m_adapter_objects.end()) {
        m_adapter_objects[obj].reset(new adapter_car(obj, this,ids, "cube_env")) ;
    }

    
}

void racing_game_logic::respond_light_info_message(ILTMessage_Read* pMsg) {
    size_t light_number = pMsg->Readuint32() ;
    for(size_t i = 0 ; i < light_number ; ++i) {
        shader_light l ;
        pMsg->ReadData(&l, sizeof(l) * 8) ;
        m_shader_mgr->light_system()->set_light(i, l) ;
    }
}



void racing_game_logic::do_render_2d(float frame_time) {
    //m_lens_flare->render() ;
    m_dash_board->render(frame_time) ;
	m_lens_flare->render();
      HLOCALOBJ obj = g_pLTClient->GetClientObject() ;
    LTVector lpos ;
    g_pLTClient->GetObjectPos(obj, &lpos) ;

	/*
    pantheios::log_DEBUG("the local pos of car is ",
                         pantheios::real(lpos.x),
                         " ", pantheios::real(lpos.y),
                         " ", pantheios::real(lpos.z)) ;
						 */

	road_node_map::single().draw();

}

void racing_game_logic::init_dash_board() {
     m_dash_board->load_screen_size(shell()->get_screen_width(),
                                 shell()->get_screen_height());
	  m_dash_board->set_name("dash1");
	  m_dash_board->load_values();
	  m_dash_board->set_speed_range(0.0f,200.0f);

}

void racing_game_logic::update_dash_board() {
    m_dash_board->set_speed(get_car_speed_KMPH()) ;
}

void racing_game_logic::on_key_down(int key, int rep) {

	static DWORD temp_ticks;
	
    
     if (key=='O')
	 {
		 /*
		 if (!road_node_maker::single().if_active())
		 {
			 road_node_maker::single().set_active(true);
		 }
		 else
			 road_node_maker::single().add_node();
		 */
	 }
	 if (key=='I')
	 {
		road_node_map::single().relocation();
		// road_node_maker::single().cancel_last();
		 
	 }
	 if (key=='U')
	 {
		// road_node_maker::single().save_to_file("waitan.node");
	 }


		 //m_sky_box->change_sky(0.01f);
	 if (key==VK_SPACE)
	 {
		// if (GetTickCount()-temp_ticks>20)
		 {
			 //if (m_carSpeedKMPH>40)
			 {
				 CAutoMessage msg ;
				 msg.Writeuint8(MSG_PLAY_FX) ;
				 msg.WriteString("RedLight") ;
				 msg.WriteObject(g_pLTClient->GetClientObject());
				 g_pLTClient->SendToServer(msg.Read(), 0) ;
				 temp_ticks=GetTickCount();		
			 }
		 }
					
	 }
}

void racing_game_logic::set_the_car_to_the_nearest_node() {
    CAutoMessage msg ;
    msg.Writeuint8(MSG_CAR_TO_NEAREST_NODE) ;
    msg.WriteLTVector(LTVector(0.0, 0.0, 0.0)) ;
    msg.WriteLTRotation(LTRotation()) ;
    g_pLTClient->SendToServer(msg.Read(), 0) ;
}

void racing_game_logic::on_key_up(int key) {
    if(key == 'M'
       || key == 'V' || key == 'G') send_key_press_message(static_cast<uint8>(key)) ;
    if(key == 'H') set_the_car_to_the_nearest_node() ;
}


void racing_game_logic::update() {
    //pantheios::log_NOTICE("call it here ?????") ;
    
	update_shader_manager();
    update_dash_board() ;
  // if(m_lens_flare->is_show()) m_lens_flare->update_position() ;
    //m_grass->update(shell()->screen_camera()->position(),shell()->screen_camera()->rotation());
    m_shader_mgr->update(g_pLTClient->GetFrameTime(), true) ;
    int i = 0;
    for(adapter_object_map_type::iterator itr = m_adapter_objects.begin() ;
        itr != m_adapter_objects.end() ;
        ++itr) {
        itr->second->update(0.0f) ;
        i++ ;
    }

   // pantheios::log_DEBUG("the adapter object number is ", pantheios::integer(i)) ;
}

void racing_game_logic::on_enter_world() {
    	m_shader_mgr->set_mode(SHADER_FULL_MOTION_BLUR);
		
		m_shadow_light.set_light_redius(20);

		m_shadow_light.set_pos(LTVector(1346,896,192));
		
		//m_shadow_light_s.set_light_redius(5000);
		m_shadow_follow.set_length(4.6f);
		m_shadow_follow.set_width(2.2f);
		m_lens_flare->set_camera(shell()->screen_camera()->camera_handle());
		m_lens_flare->set_sun_pos(LTVector(1000,106,392)*0.1f);
		road_node_map::single().set_window_size(shell()->get_screen_width(),
			shell()->get_screen_height());
		road_node_map::single().load_road_info("waitan.node");
		road_node_map::single().relocation();
		//m_grass->load_dates();


		
		
	
}


void racing_game_logic::render_for_shader() {
	
    
    //m_grass->render() ;
	m_shadow_follow.render();
    m_trace_pool->render() ;

	m_sky_box->render() ;
	m_lens_flare->render();
	road_node_maker::single().update();
	//road_node_map::single().debug_render();
	//m_grass->sort();
	//m_grass->render();
	
}

void racing_game_logic::update_shader_manager() {
    m_curtime = g_pLTClient->GetGameTime();
    //m_curtime += 0.005f ;
   
}

racing_game_logic::racing_game_logic(CLTClientShell* s, const std::string& level_name) :
    game_logic(s),
    m_level_name(level_name),
    m_carSpeedKMPH(0.0f),
    m_shader_mgr(new shader_manager(s)),
    m_sky_box(new sky_box),
    m_trace_pool(new trace_pool),
    m_dash_board(new dash_board),
    m_lens_flare(new lens_flare),
    m_freeCameraType(0),
    m_curtime(0.0f),
    m_lasttime(0.0f),
	m_blur_seter(new blur_seter(0)),
	m_video_mgr(new Video_mgr),
	m_speaker("audio/sound/shoot.wav"),
	m_engine_speaker("audio/sound/engine.wav"),
	m_back_sound("audio/music/Space Boy.wav"),
	m_brake_sound("audio/sound/brake.wav"),
	m_back_sound_played(false)
	//m_grass(new grass_control)
	{
    pantheios::log_ALERT("create racing_game_logic") ;
    m_shader_mgr->set_camera(shell()->screen_camera()->camera_handle()) ;

    m_message_manager.add(MSG_DEMOROOM_INFO, this,
                          &racing_game_logic::respond_demoroom_info_message) ;
    m_message_manager.add(MSG_STARTPOINT_POS_ROT, shell(),
                          &CLTClientShell::respond_startpoint_pos_rot_message) ;
    m_message_manager.add(MSG_WORLD_PROPS, shell(),
                          &CLTClientShell::respond_world_props_messge) ;
    m_message_manager.add(MSG_CAR_TYREMARKS, this,
                          &racing_game_logic::respond_car_tyremarks_message) ;
    m_message_manager.add(MSG_CAR_BASE_INFO, this,
                          &racing_game_logic::respond_car_base_info_message) ;
    m_message_manager.add(MSG_TEST, this,
                          &racing_game_logic::respond_test_message) ;
    m_message_manager.add(MSG_BINDING_SHADER, this,
                          &racing_game_logic::respond_bind_shader_message) ;

    m_message_manager.add(MSG_SHADER_UPDATE_MESSAGE, this,
                          &racing_game_logic::respond_shader_update_message) ;
    m_message_manager.add(MSG_LIGHT_INFO_MESSAGE, this,
                          &racing_game_logic::respond_light_info_message) ;

    m_message_manager.add(MSG_RECEIVE_OTHER_CAR_MESSAGE,
                          this,
                          &racing_game_logic::respond_receive_ohter_car_message) ;
    
    pantheios::log_ALERT("now that is ok.......................") ;
    
    m_shader_mgr->init() ;
    pantheios::log_ALERT("here is ok????????????????") ;
    

	m_sky_box->init();

	init_dash_board();
	m_shader_mgr->bind_seters(SHADER_FULL_MOTION_BLUR,m_blur_seter.get());


	m_speaker.set_interval(200);
	m_speaker.set_volume(40);
	m_engine_speaker.set_interval(400);
	

	m_back_sound.set_loop(true);
	m_back_sound.set_volume(60);

	m_brake_sound.set_volume(50);
	m_brake_sound.set_interval(200);
	
	

	


    init_level_infos() ;
    pantheios::log_ALERT("create the game logic.") ;
    

	
}

void racing_game_logic::init_level_infos() {
    using namespace Utility ;
    std::string darkers = page_info_getter::Instance().
        get<std::string>(m_level_name, "info", "darkers");
    property_keys_type keys ;
    page_info_getter::Instance().get_keys(darkers, keys) ;
    std::for_each(keys.begin(), keys.end(),
                  boost::bind(&racing_game_logic::read_a_darker_info, this,
                              boost::cref(darkers), _1)) ;
    std::string lighters = page_info_getter::Instance().
        get<std::string>(m_level_name, "info", "lights") ;
    page_info_getter::Instance().get_keys(lighters, keys) ;
    std::for_each(keys.begin(), keys.end(),
                  boost::bind(&racing_game_logic::read_a_light_info, this,
                              boost::cref(lighters) , _1));
}

void racing_game_logic::read_a_light_info(const std::string& lights, const std::string& light_name) {
    using namespace Utility ;
    normal_number_range pos = page_info_getter::Instance().
        get(lights, light_name, "position", normal_number_range(0.0, 0.0, 0.0) ) ;
    normal_number_range color = page_info_getter::Instance().
        get(lights, light_name, "color", normal_number_range(0.0, 0.0, 0.0)) ;
    normal_number_range dir = page_info_getter::Instance().
        get(lights, light_name, "direction", normal_number_range(0.0, 0.0, 0.0)) ;
    int is_sun_like = page_info_getter::Instance().
        get(lights, light_name, "is_sun_like", 0) ;
    double constance_attenuation = page_info_getter::Instance().
        get(lights, light_name, "constance_attenuation", 1.0) ;
    double linear_attenuation = page_info_getter::Instance().
        get(lights, light_name, "linear_attenuation", 0.0) ;
    double quadratic_attenuation = page_info_getter::Instance().
        get(lights, light_name, "quadratic_attenuation", 0.0) ;
    shader_light l ;
    l.Position[0] = pos[0] ;
    l.Position[1] = pos[1] ;
    l.Position[2] = pos[2] ;
    if(is_sun_like) l.Position[3] = 1.0f ;
    l.Color[0] = color[0] ;
    l.Color[1] = color[1] ;
    l.Color[2] = color[2] ;
    l.Color[3] = 1.0f ; // alpha always 1.0
    l.SpotDirection[0] = dir[0] ;
    l.SpotDirection[1] = dir[1] ;
    l.SpotDirection[2] = dir[2] ;
    l.ConstanceAttenuation = constance_attenuation ;
    l.LinearAttenuation = linear_attenuation ;
    l.QuadraticAttenuation = quadratic_attenuation ;
    m_shader_mgr->light_system()->add_light(l) ;
}

void racing_game_logic::read_a_darker_info(const std::string& darkers, const std::string& darker_name) {
    using namespace Utility ;
    normal_number_range pos = page_info_getter::Instance().
        get(darkers, darker_name, "position", normal_number_range(0.0, 0.0, 0.0)) ;
    int group_id = page_info_getter::Instance().get
        (darkers, darker_name, "group_id", 0) ;
    float radius = static_cast<float>(
        page_info_getter::Instance().get(
            darkers, darker_name, "radius", 100.0)) ;
    float factor = static_cast<float>(
        page_info_getter::Instance().
        get(darkers, darker_name, "factor", 1.0)) ;
    m_darker_ojbects.push_back(darker_object(LTVector(pos[0], pos[1], pos[2]),
                                             radius,
                                             group_id,
                                             factor)) ;
}

void racing_game_logic::respond_receive_ohter_car_message(ILTMessage_Read* pMsg) {
    uint32 number = pMsg->Readuint32() ;
    for(uint32 i = 0 ; i < number ; ++i) {
		HOBJECT tempo=pMsg->ReadObject();
		if (tempo!=g_pLTClient->GetClientObject())
		{
			road_node_map::single().add_player(tempo,"xx");

		}

        //m_others.insert(pMsg->ReadObject()) ;
    }

	
}

void racing_game_logic::respond_shader_update_message(ILTMessage_Read* pMsg) {
    // FIXME
    // now just a test
    HOBJECT obj = pMsg->ReadObject() ;
    if(m_adapter_objects.find(obj) != m_adapter_objects.end()) {
        m_adapter_objects[obj]->on_message(pMsg) ;
    }
}

void racing_game_logic::respond_demoroom_info_message(ILTMessage_Read* pMsg) {
    LTVector position = pMsg->ReadLTVector() ;
	shell()->screen_camera()->set_position(position);

}

void racing_game_logic::respond_test_message(ILTMessage_Read* pMsg) {
    uint32 timeOld = pMsg->Readuint32() ;
    char maxInfo[512] ;
    pMsg->ReadString(maxInfo, sizeof(maxInfo)) ;
    int32 idd = pMsg->Readint32() ;
    CAutoMessage msg ;
    msg.Writeuint8(MSG_TEST) ;

    msg.Writeuint32(timeOld) ;
    msg.Writeuint32(strlen(maxInfo)) ;
    msg.Writeint32(idd) ;
    g_pLTClient->SendToServer(msg.Read(), 0) ;
}

void racing_game_logic::respond_car_base_info_message(ILTMessage_Read* pMsg) {
	
	static DWORD m_fire_ticks=0;
	static DWORD m_engine_ticks=0;

//  LTVector pos = pMsg->ReadLTVector() ;
//  LTRotation rot = pMsg->ReadLTRotation() ;
    
  //m_car_position = pos;
  //m_car_rotation = rot;

	m_shadow_light.update();
	//m_shadow_follow.set_trans(pos,rot);
	





	if (!m_back_sound_played)
	{
		m_back_sound.play_sound();
		m_back_sound_played=true;
	}

    m_carSpeedKMPH = pMsg->Readfloat() ;
	m_blur_seter->set_speed(m_carSpeedKMPH);
	//m_speaker.set_position(pos);
	//m_speaker.set_volume(m_carSpeedKMPH/4.0f+5);
	m_engine_speaker.set_volume(m_carSpeedKMPH/4.0f+5);
	m_engine_speaker.play_sound();
	/*
	if (m_carSpeedKMPH>180&&GetTickCount()-m_fire_ticks>150)
	{
		CAutoMessage msg ;
		msg.Writeuint8(MSG_PLAY_FX) ;
		msg.WriteString("Ngas") ;
		msg.WriteObject(g_pLTClient->GetClientObject());
		g_pLTClient->SendToServer(msg.Read(), 0) ;
		m_fire_ticks=GetTickCount();
		m_speaker.play_sound();
	}
	*/
    LTVector cameraPos = pMsg->ReadLTVector();
    LTRotation cameraRot = pMsg->ReadLTRotation();
    //cameraPos += cameraRot.Up() * 3.0f ;
/*

    LTVector objPos = pos - rot.Forward() * 3.0f;
    m_objPos = objPos;
    m_objRot = rot;

    LTRotation backrot=rot;
    LTVector  backpos=pos;
    backrot.Rotate(backrot.Up(),3.1415926f);
    backpos+=backrot.Up()* 2.0f;
    backpos-=backrot.Forward()*5.0f;

*/
    if (!m_freeCameraType) shell()->screen_camera()->set_position_and_rotation(cameraPos, cameraRot) ;


}

void racing_game_logic::send_key_press_message(uint8 key) {
    CAutoMessage msg ;
    msg.Writeuint8(MSG_SEND_KEY_PRESS) ;
    msg.Writeuint8(key);
    g_pLTClient->SendToServer(msg.Read(), NULL) ;
    
    
    
}


void racing_game_logic::on_effect_shader_set_params(LTEffectShader* pEffect,
                                                     CRenderStyle* pRenderStyle,
                                                     HOBJECT hObj,
                                                     const LTShaderDeviceState& ShaderDeviceState) {
    m_shader_mgr->on_effect_shader_set_params(pEffect,
                                              pRenderStyle,
                                              hObj,
                                              ShaderDeviceState) ;
}

void racing_game_logic::respond_car_tyremarks_message(ILTMessage_Read* pMsg) {
	static DWORD shift_ticks;

	if (m_carSpeedKMPH>20)
	{
		if (GetTickCount()-shift_ticks>200)
		{

			CAutoMessage msg ;
			msg.Writeuint8(MSG_PLAY_FX) ;
			msg.WriteString("ShiftSmoke") ;
			msg.WriteObject(g_pLTClient->GetClientObject());
			g_pLTClient->SendToServer(msg.Read(), 0) ;
			shift_ticks=GetTickCount();
		}
	}
      
    
	m_brake_sound.play_sound();
    uint32 num_of_car = pMsg->Readuint32() ;
    //pantheios::log_DEBUG("s -> c generate the car tyremarks") ;
    for(int i = 0 ; i < num_of_car ; ++i) {
        
        uint32 num_of_marks = pMsg->Readuint32() ;
        for(int j = 0 ; j < num_of_marks ; ++j) {
            LTVector leftUp = pMsg->ReadCompLTVector() ;
            LTVector rightUp = pMsg->ReadCompLTVector() ;
            LTVector leftDown = pMsg->ReadCompLTVector() ;
            LTVector rightDown = pMsg->ReadCompLTVector() ;
            float alpha = pMsg->Readfloat() ;

            m_trace_pool->push(leftUp,
                               rightUp,
                               rightDown,
                               leftDown,
                               alpha) ;
        }
    }
}

struct compare_darker {
    compare_darker(const LTVector& car_pos) : m_car_pos(car_pos) {}
    const LTVector& m_car_pos ;
    bool operator()(const darker_object& lhs,
                    const darker_object& rhs) const {
        const LTVector& lhs_pos = boost::get<DOE_POSITION>(lhs) ;
        float lhs_radius = boost::get<DOE_RADIUS>(lhs) ;
        float rhs_radius = boost::get<DOE_RADIUS>(rhs) ;
        const LTVector& rhs_pos = boost::get<DOE_POSITION>(rhs) ;
        float dist_lhs = lhs_pos.DistSqr(m_car_pos) ;
        float dist_rhs = rhs_pos.DistSqr(m_car_pos) ;
        if(dist_lhs > lhs_radius * lhs_radius) return false ;
        if(dist_rhs > rhs_radius * rhs_radius) return true ;
        return dist_lhs < dist_rhs ;
    }
} ;


darker_object racing_game_logic::get_darker_factor(HOBJECT obj) {
    if(m_darker_ojbects.empty()) return  darker_object(LTVector(), 0.0, 0, 0.0) ;
    LTVector pos ;
    g_pLTClient->GetObjectPos(obj, &pos) ;
    std::sort(m_darker_ojbects.begin(),
              m_darker_ojbects.end(),
              compare_darker(pos)) ;
    //float radius = boost::get<DOE_RADIUS>(m_darker_ojbects[0]) ;
    //float disSqr = pos.DistSqr(boost::get<DOE_POSITION>(m_darker_ojbects[0])) ;
    //if(disSqr > radius * radius) return darker_object(LTVector(), 0.0, 0, 0.0) ;
    return m_darker_ojbects[0] ;
    
}

void racing_game_logic::caculate_the_nearest_darker() {
    HLOCALOBJ obj = g_pLTClient->GetClientObject() ;
    LTVector pos ;
    g_pLTClient->GetObjectPos(obj, &pos) ;
    std::sort(m_darker_ojbects.begin(),
              m_darker_ojbects.end(),
              compare_darker(pos)) ;
}
